#include "alp_iconv.h"
#include "alp.h"

#ifndef ALP_ICONV_MLANG

#include <iconv.h>

struct alp_iconv_t
{
	iconv_t cd;
};

static alp_status_t alp_iconv_cleanup(void *ic)
{
	if (iconv_close(((alp_iconv_t *)ic)->cd))
		return errno;
	return 0;
}

alp_status_t alp_iconv_create(alp_iconv_t **newic, const char* fromcode, const char* tocode)
{
	alp_iconv_t *ic;
	iconv_t cd;
	*newic = 0;
	cd = iconv_open(tocode, fromcode);
	if (cd == 0)
		return errno;
	//ic = alp_palloc(pool, sizeof(alp_iconv_t));
	ic = (alp_iconv_t*)malloc(sizeof(alp_iconv_t));
	if (ic == 0)
	{
		iconv_close(cd);
		return ENOMEM;
	}
	ic->cd = cd;
	//alp_pool_cleanup_register(pool, ic, alp_iconv_cleanup);
	*newic = ic;
	return 0;
}

alp_status_t alp_iconv(alp_iconv_t *ic, const char *inbuf, size_t *inlen, char *outbuf, size_t *outlen)
{
	char *src = (char *)inbuf, *tgt = outbuf;
	size_t inleft = *inlen, outleft = *outlen;
	size_t size = iconv(ic->cd, &src, &inleft, &tgt, &outleft);
	if (size == (size_t)-1)
		return errno;
	*inlen -= inleft;
	*outlen -= outleft;
	return 0;
}

#else

#include <mlang.h>

struct alp_iconv_t
{
	IMLangConvertCharset *cc;
};

static alp_status_t alp_iconv_cleanup(void *iconv)
{
	IMLangConvertCharset *cc = ((alp_iconv_t *)iconv)->cc;
	cc->lpVtbl->Release(cc);
	return 0;
}

static struct _CodePageMap
{
	const char *name;
	UINT cp;
}const cpmap[] = {
	{"shift_jis", 932},
	{"euc-jp", 51932},
	{"gb2312", 936}, {"gbk", 936}, {"gb2312-80", 936}, {"gb2312-1980", 936},
	{"x-cp20936", 20936},
	{"euc-cn", 51926},
	{"hz-gb-2312", 52936},
	{"gb18030", 54936},
	{"ksc5601", 949}, {"ks_c_5601-1987", 949},
	{"euc-kr", 51949},
	{"big5", 950},
	{"euc-tw", 51950},
	{"cp037", 37}, {"ibm037", 37},
	{"cp1250", 1250}, {"windows-1250", 1250},
	{"cp1251", 1251}, {"windows-1251", 1251},
	{"cp1252", 1252}, {"windows-1252", 1252},
	{"cp1253", 1253}, {"windows-1253", 1253},
	{"cp1254", 1254}, {"windows-1254", 1254},
	{"cp1255", 1255}, {"windows-1255", 1255},
	{"cp1256", 1256}, {"windows-1256", 1256},
	{"cp1257", 1257}, {"windows-1257", 1257},
	{"cp1258", 1258}, {"windows-1258", 1258},
	{"macintosh", 10000}, {"mac", 10000},
	{"ascii", 20127}, {"us-ascii", 20127},
	{"koi8-r", 20866},
	{"iso-8859-1", 28591},
	{"iso-8859-2", 28592},
	{"iso-8859-3", 28593},
	{"iso-8859-4", 28594},
	{"iso-8859-5", 28595},
	{"iso-8859-6", 28596},
	{"iso-8859-7", 28597},
	{"iso-8859-8", 28598},
	{"iso-8859-9", 28599},
	{"iso-8859-13", 28603},
	{"iso-8859-15", 28605},
	{"utf-7", 65000},
	{"utf-8", 65001}, {"utf8", 65001},
	{"utf-16", 1200}, {"utf-16le", 1200},
	{"utf-16be", 1201}, {"unicodeFFFE", 1201},
	{"utf-32", 12000},
	{"utf-32be", 12001},
	{0, 0}
};

static UINT codepage(const char *name)
{
	const struct _CodePageMap *p = cpmap;
	for (; p->name; ++p)
		if (_stricmp(name, p->name) == 0)
			break;
	return p->cp;
}

alp_status_t alp_iconv_create(alp_iconv_t **newic, const char* fromcode, const char* tocode)
{
	UINT from = codepage(fromcode), to = codepage(tocode);
	IMLangConvertCharset *cc = 0;
	alp_iconv_t *ic;
	*newic = 0;
	if (from == 0 || to == 0)
		return EINVAL;
	//ic = (alp_iconv_t *)alp_palloc(pool, sizeof(alp_iconv_t));
	ic = (alp_iconv_t*) malloc(sizeof(alp_iconv_t));
	if (ic == 0)
		return ENOMEM;
	CoCreateInstance(&CLSID_CMLangConvertCharset, 0, CLSCTX_INPROC_SERVER, &IID_IMLangConvertCharset, &cc);
	if (FAILED(cc->lpVtbl->Initialize(cc, from, to, 0)))
	{
		cc->lpVtbl->Release(cc);
		return EINVAL;
	}
	ic->cc = cc;
	//alp_pool_cleanup_register(pool, ic, alp_iconv_cleanup);
	*newic = ic;
	return 0;
}

alp_status_t alp_iconv(alp_iconv_t *ic, const char *inbuf, size_t *inlen, char *outbuf, size_t *outlen)
{
	// (size_t * -> UINT *) should be OK for little-endian arch
	IMLangConvertCharset *cc = ic->cc;
	HRESULT hr = cc->lpVtbl->DoConversion(cc, (BYTE *)inbuf, (UINT *)inlen, (BYTE *)outbuf, (UINT *)outlen);
	return 0;
}

#endif // ALP_ICONV_LIBICONV

alp_status_t alp_iconv_destroy(alp_iconv_t *ic)
{
	IMLangConvertCharset *cc = ic->cc;
	cc->lpVtbl->Release(cc);
	return 0;
	//return alp_pool_cleanup_run(ic->pool, ic, alp_iconv_cleanup);
}
/*
alp_pool_t * alp_iconv_pool_get(alp_iconv_t *ic)
{
	return ic->pool;
}
*/